﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;

using AForge.Video;
using AForge.Video.DirectShow;

using Microsoft.DirectX;

using Graphene.Processing;
using Graphene.Storing;

namespace Graphene
{
    public delegate AForge.Video.DirectShow.FilterInfo GetVideoSourceSelectionDelegate();        
 
    /**
     * Holds together the application.
     * 
     * Itself has no significant processing (time consuming) code.
     * 
     * Acts like a component database or object central for the software.
     * 
     * Model class of the Model-View-Controller architecture.
     */ 
    public class Model
    {
        private static readonly Random RANDOM = new Random();

        public Model()
        {            
            this.videoSources = new Dictionary<string, VideoSource>();
            this.imageProcessors = new Dictionary<string, MultiColorImageProcessor>();
            this.imageProcessorThreads = new Dictionary<string, Thread>();

            RefreshAvailableVideoDeviceList();

            this.dXCameraPosition = new Vector3(2.0f, 4.0f, -10.0f);
            this.dXCameraDirection = new Vector3(0.0f, -0.05f, 1.0f);

            this.markerSamplesFor3DMarkerCalibration = new Dictionary<MarkerInImage, MarkerInImage>();            
            this.calibratingMarkerPrimarySelected = false;
            this.calibratingMarkerSecondarySelected = false;
            this.tolerancePrimaryForMarkerCalibration = 19;
            this.toleranceSecondaryForMarkerCalibration = 19;
            this.sampleBadnessPrimary = 1.0f;
            this.sampleBadnessSecondary = 1.0f;

            this.markersFor3D = new Dictionary<MarkerInImage, MarkerInImage>();
        }

        /**
         * Resets and fills the availableVideoDeviceList with the current devices attached to the computer.
         */
        public void RefreshAvailableVideoDeviceList()
        {            
            this.availableVideoDeviceList = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        }

        public FilterInfoCollection GetAvailableVideoDeviceList()
        {
            return this.availableVideoDeviceList;
        }

        public FilterInfo SearchVideoDeviceByName(string name)
        {
            foreach (FilterInfo filterInfo in availableVideoDeviceList)
            {
                if (filterInfo.Name.Equals(name))
                {
                    return filterInfo;
                }
            }

            return null;
        }

        /**
         * Initializes a new camera or video input device, and adds it to our list of cameras.
         */
        public string InitVideoSource(string deviceId, string myId, string myName)
        {            
            foreach (VideoSource videoSource in this.videoSources.Values)
            {
                if (videoSource.MyId.Equals(myId)
                    || videoSource.DeviceId.Equals(deviceId))
                {
                    return null;
                }
            }
            VideoSource newVideoSource = new VideoSource(this, deviceId, myId, myName);
            this.videoSources.Add(newVideoSource.MyId, newVideoSource);             
            
            return newVideoSource.MyId;
        }

        /**
         * States whether a given video device was already initialized in the model or not.
         */
        public bool IsVideoSourceInitialized(string deviceId)
        {
            return this.videoSources.ContainsKey(deviceId);
        }

        /**
         * Camera starts capturing.
         */
        public void StartVideoSource(string myId)
        {
            if (this.videoSources.ContainsKey(myId))
            {
                this.videoSources[myId].Start();
            }
        }

        /**
         * States if the camera is capturing.
         */
        public bool IsCameraRunning(string myId)
        {
            if (this.videoSources.ContainsKey(myId))
            {
                return this.videoSources[myId].IsRunning();
            }

            return false;
        }

        /**
         * Camera stops capturing.
         */ 
        public void StopVideoSource(string myId)
        {
            if (this.videoSources.ContainsKey(myId))
            {
                this.videoSources[myId].Stop();
            }
        }

        /**
         * Camrea stops capturing.
         * Camera is removed from our video source list. If wanted to be capturing again,
         * it will have to be initialized again.
         */
        public void CloseVideoSource(string myId)
        {
            if (this.videoSources.ContainsKey(myId))
            {
                this.videoSources[myId].Close();
                this.videoSources.Remove(myId);
            }
        }

        /**
         * Closes all VideoSources, and empties the videoSources list.
         */
        public void CloseAllVideoSources()
        {
            foreach (VideoSource vs in this.videoSources.Values)
            {
                vs.Close();
            }
            this.videoSources.Clear();
        }

        /**
         * When the given camera captures a new image the give event handler will be called up if added before here.
         */
        public void AddResultChangedHandlerToVideoSource(string deviceId, Graphene.Processing.ResultChangedHandler newFrameEventHandler)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                this.videoSources[deviceId].AddResultChangedHandler(newFrameEventHandler);
            }            
        }

        public void RemoveResultChangedHandlerToVideoSource(string deviceId, Graphene.Processing.ResultChangedHandler newFrameEventHandler)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                this.videoSources[deviceId].RemoveResultChangedHandler(newFrameEventHandler);
            }
        }

        /**
         * You guess!
         */ 
        public byte[] GetByteImageFromVideoSource(string deviceId)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                return (byte[])this.videoSources[deviceId].ResultData;
            }

            return null;
        }

        /**
         * You guess!
         */
        public Bitmap GetBitmapFromVideoSource(string deviceId)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                return this.videoSources[deviceId].Bitmap;
            }

            return null;
        }

        /**
         * You guess!
         */
        public int GetNFramesCapturedFromVideoSource(string deviceId)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                return this.videoSources[deviceId].NResultChanges;
            }

            return 0;
        }

        /**
         * You guess!
         */
        public void ResetNFramesCapturedFromVideoSource(string deviceId)
        {
            if (this.videoSources.ContainsKey(deviceId))
            {
                this.videoSources[deviceId].ResetNResultChanges();
            }            
        }

        public string SearchVideoSourceIdByName(string myName)
        {
            foreach (VideoSource videoSource in this.videoSources.Values)
            {
                if (videoSource.MyName.Equals(myName))
                {
                    return videoSource.MyId;
                }
            }

            return null;
        }

        /**
         * You guess!
         */
        public int GetNFramesProcessedFromImageProcessor(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return this.imageProcessors[deviceId].NProcessingCycles;
            }

            return 0;
        }

        /**
         * You guess!
         */
        public void ResetNFramesProcessedInImageProcessor(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                this.imageProcessors[deviceId].ResetNProcessingCycles();
            }
        }

        /**
         * Initializes an image processor for a given capturing device and starts it as a new thread.         
         * Each video capturing device must have its own processor because different images give different
         * results, and they must run parallel.
         */
        public string InitImageProcessor(string videoSourceId, string myId, string myName)
        {
            if (!this.videoSources.ContainsKey(videoSourceId))
            {
                return null;
            }
            
            MultiColorImageProcessor newImageProcessor = new MultiColorImageProcessor(this, this.videoSources[videoSourceId], myId, myName);
            this.imageProcessors.Add(newImageProcessor.MyId, newImageProcessor);
            string threadId = "Thread-" + myId;
            Thread newThread = new Thread(new ThreadStart(newImageProcessor.MyRun));
            newThread.Name = threadId;
            this.imageProcessorThreads.Add(newThread.Name, newThread);
            newThread.Start();
                
            return newImageProcessor.MyId;
        }

        /**
         * Returns whether an ImageProcessor for a given VideoDevice is initialized.
         */ 
        public bool IsImageProcessorInitialized(string myId)
        {
            return (this.imageProcessors.ContainsKey(myId) && this.imageProcessorThreads.ContainsKey(myId));
        }

        /**
         * Lets the ImageProcessor's thread run.
         */ 
        public void StartImageProcessor(string myId)
        {
            if (this.imageProcessors.ContainsKey(myId))
            {
                this.imageProcessors[myId].MyStart();
            }
        }

        /**
         * You guess!
         */
        public bool IsImageProcessorRunning(string myId)
        {
            if (this.imageProcessors.ContainsKey(myId))
            {
                return this.imageProcessors[myId].IsRunning();
            }

            return false;
        }

        /**
         * Stops ImageProcessor's thread running.
         */ 
        public void StopImageProcessor(string myId)
        {
            if (this.imageProcessors.ContainsKey(myId))
            {
                this.imageProcessors[myId].MyStop();
            }
        }

        /**
         * Stops an ImageProcessor and its thread, and removes them from our processors list.
         */ 
        public void CloseImageProcessor(string myId)
        {
            if (!this.imageProcessors.ContainsKey(myId))
            {
                return;
            }

            this.imageProcessors[myId].MyStop();
            this.imageProcessorThreads["Thread-" + myId].Abort();
            // Maybe this is optional.
            this.imageProcessors[myId].AssignedProcessorSource.RemoveResultChangedHandler(this.imageProcessors[myId].SetSourceToChanged);
            this.imageProcessorThreads.Remove("Thread-" + myId);
            this.imageProcessors.Remove(myId);
        }


        /**
         * Stops all ImageProcessors and their threads, and empties the processor list.
         */
        public void CloseAllImageProcessors()        
        {
            foreach (MultiColorImageProcessor processor in this.imageProcessors.Values)
            {
                processor.MyStop();
                // Maybe this is optional.
                processor.AssignedProcessorSource.RemoveResultChangedHandler(processor.SetSourceToChanged);
            }
            foreach (Thread thread in this.imageProcessorThreads.Values)
            {
                thread.Abort();                
            }
            this.imageProcessorThreads.Clear();
            this.imageProcessors.Clear();
        }

        /**
         * When the ImageProcessor connected to the VideoDevice with the corresponding deviceId computes a new result,
         * the given eventhandler will be called.
         */ 
        public void AddProcessedHandlerToImageProcessor(string myId, Graphene.Processing.ProcessedHandler processedHandler)
        {
            if (!this.imageProcessors.ContainsKey(myId))
            {
                return;
            }

            this.imageProcessors[myId].AddProcessedHandler(processedHandler);
        }

        public void RemoveProcessedHandlerToImageProcessor(string myId, Graphene.Processing.ProcessedHandler processedHandler)
        {
            if (!this.imageProcessors.ContainsKey(myId))
            {
                return;
            }

            this.imageProcessors[myId].RemoveProcessedHandler(processedHandler);
        }

        /**
         * When the deviceId ImageProcessor states that the markers just have changed, the method specified as second argument
         * will be called (that's a notification in that method there).
         */
        public void AddResultChangedHandlerToImageProcessor(string myId, Graphene.Processing.ResultChangedHandler markersChangedHandler)
        {
            if (!this.imageProcessors.ContainsKey(myId))
            {
                return;
            }

            this.imageProcessors[myId].AddResultChangedHandler(markersChangedHandler);
        }

        public void RemoveResultChangedHandlerToImageProcessor(string myId, Graphene.Processing.ResultChangedHandler markersChangedHandler)
        {
            if (!this.imageProcessors.ContainsKey(myId))
            {
                return;
            }

            this.imageProcessors[myId].RemoveResultChangedHandler(markersChangedHandler);
        }

        /**
         * You guess!
         */
        public Dictionary<string, MarkerInImage> GetMarkersFromImageProcessor(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return (Dictionary<string, MarkerInImage>)this.imageProcessors[deviceId].ResultData;
            }

            return null;
        }
        
        /**
         * You guess!
         */
        public Dictionary<string, Spat> GetSpatsFromImageProcessor(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return this.imageProcessors[deviceId].Spats;
            }

            return null;
        }

        /**
         * You guess!
         */
        public Dictionary<string, PointInt2D> GetFirstPixelsFoundFromImageProcessor(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return this.imageProcessors[deviceId].FirstPixelsFound;
            }

            return null;
        }

        public string SearchImageProcessorIdByName(string myName)
        {
            foreach (MultiColorImageProcessor imageProcessor in this.imageProcessors.Values)
            {
                if (imageProcessor.MyName.Equals(myName))
                {
                    return imageProcessor.MyId;
                }
            }

            return null;
        }

        public Dictionary<string, MarkerInSpace> GetMarkersFromThreeDProcessor()
        {
            if (this._3DProcessor != null)
            {
                return (Dictionary<string, MarkerInSpace>)this._3DProcessor.ResultData;
            }

            return null;
        }

        /**
         * You guess!
         * Processors and VideoDevices have their own different images.
         */
        public byte[] GetProcessorByteImage(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return (byte[])this.imageProcessors[deviceId].CopiedSourceData;
            }

            return null;
        }

        /**
         * You guess!         
         */
        public double GetProcessorAvgSearchTime(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return this.imageProcessors[deviceId].AverageProcessTime;
            }

            return -1.0;
        }

        /**
         * You guess!         
         */
        public int GetProcessorLongestSearchTime(string deviceId)
        {
            if (this.imageProcessors.ContainsKey(deviceId))
            {
                return this.imageProcessors[deviceId].LongestProcessTime;
            }

            return -1;
        }

        /*
         * Creates the 3DProcessor and its thread from the two 2DProcessor sources.
         */
        public void Init3DProcessor()
        {
            if (this.sourceProcessor1For3DSelection != null && this.sourceProcessor2For3DSelection != null)
            {
                this._3DProcessor = new Processor3D(this, this.sourceProcessor1For3DSelection, this.sourceProcessor2For3DSelection);
                this.sourceProcessor1For3DSelection.AddResultChangedHandler(this._3DProcessor.SetSourceToChanged);
                this.sourceProcessor2For3DSelection.AddResultChangedHandler(this._3DProcessor.SetSourceToChanged);
                this._3DProcessorThread = new Thread(new ThreadStart(this._3DProcessor.MyRun));
            }
        }

        /*
         * Makes sure the 3DProcessor's thread is started and lets it enter the processing loop.
         */
        public void Start3DProcessor()
        {
            if (this._3DProcessorThread != null && !this._3DProcessorThread.IsAlive)
            {
                this._3DProcessorThread.Start();
            }
        }

        /*
         * Makes the 3DProcessor to jump over the processing loop.
         */
        public void Pause3DProcessor()
        {
            if (this._3DProcessorThread != null && this._3DProcessorThread.IsAlive)
            {
                this._3DProcessorThread.Abort();
            }
        }

        private void UpdateDXCameraVelocity()
        {
            float CAMERA_SPEED = 0.01f;

            this.UpdateDXCameraPosition();

            dxCameraStopwatch.Stop();

            Vector3 cameraMovingDirection = new Vector3();
            if (this.dXCameraIsMovingForward)
            {
                cameraMovingDirection += this.dXCameraDirection;
            }
            if (this.dXCameraIsMovingBackward)
            {
                cameraMovingDirection -= this.dXCameraDirection;
            }
            if (this.dXCameraIsStrafingRight)
            {
                cameraMovingDirection += new Vector3(this.dXCameraDirection.Z, 0, -this.dXCameraDirection.X);
            }
            if (this.dXCameraIsStrafingLeft)
            {
                cameraMovingDirection -= new Vector3(this.dXCameraDirection.Z, 0, -this.dXCameraDirection.X);
            }
            cameraMovingDirection.Normalize();
            this.dXCameraVelocity = cameraMovingDirection * CAMERA_SPEED;

            dxCameraStopwatch.Restart();
        }

        private void UpdateDXCameraPosition()
        {
            dxCameraStopwatch.Stop();
            this.dXCameraPosition += this.dXCameraVelocity * dxCameraStopwatch.ElapsedMilliseconds;
            dxCameraStopwatch.Restart();
        }

        public void SetCameraPositionAndAngle(string videoSourceId, PointReal3D position, PointReal2D angle)
        {            
            this.videoSources[videoSourceId].PositionInWorld = position;
            this.videoSources[videoSourceId].AngleInWorld = angle;
        }

        public int CountOfListOfMarkerSamplesFor3DMarkerCalibration()
        {
            return this.markerSamplesFor3DMarkerCalibration.Count;
        }

        public void AddSampleToListForMarkerCalibration(MarkerInImage markerPrimary, MarkerInImage markerSecondary)
        {            
            this.markerSamplesFor3DMarkerCalibration.Add(markerPrimary, markerSecondary);
        }

        public KeyValuePair<MarkerInImage, MarkerInImage> ElementAtSampleListForMarkerCalibration(int i)
        {
            List<MarkerInImage> keyList = new List<MarkerInImage>(this.markerSamplesFor3DMarkerCalibration.Keys);
            MarkerInImage markerSamplePrimary = keyList[i];
            MarkerInImage markerSampleSecondary = this.markerSamplesFor3DMarkerCalibration[markerSamplePrimary];

            return (new KeyValuePair<MarkerInImage, MarkerInImage>(markerSamplePrimary, markerSampleSecondary));
        }

        public void RemoveSampleFromListForMarkerCalibration(string primaryId, string secondaryId)
        {
            /*
            MarkerInImage fakePrimary = new MarkerInImage(primaryId, primaryId, Color.Black);
            MarkerInImage fakeSecondary = new MarkerInImage(secondaryId, secondaryId, Color.Black);
             */
            MarkerInImage matchingMarker = null;
            foreach (MarkerInImage markerPrimary in this.markerSamplesFor3DMarkerCalibration.Keys)
            {
                MarkerInImage markerSecondary = this.markerSamplesFor3DMarkerCalibration[markerPrimary];
                if (markerPrimary.MyId.Equals(primaryId)
                    && markerSecondary.MyId.Equals(secondaryId))
                {
                    matchingMarker = markerPrimary;                    
                }
            }
            if (matchingMarker != null)
            {
                this.markerSamplesFor3DMarkerCalibration.Remove(matchingMarker);
            }

            /*
            if (this.markerSamplesFor3DMarkerCalibration.ContainsKey(fakePrimary)
                && this.markerSamplesFor3DMarkerCalibration.ContainsValue(fakeSecondary))
            {
                this.markerSamplesFor3DMarkerCalibration.Remove(fakePrimary);
            }
             * */
        }

        public void ClearSamplesForMarkerCalibration()
        {
            this.markerSamplesFor3DMarkerCalibration.Clear();
        }

        public KeyValuePair<MarkerInImage, MarkerInImage> GetAverageFromListFor3DMarkerCalibration()
        {
            int colorRSumPrimary = 0;
            int colorGSumPrimary = 0;
            int colorBSumPrimary = 0;
            int toleranceSumPrimary = 0;
            int colorRSumSecondary = 0;
            int colorGSumSecondary = 0;
            int colorBSumSecondary = 0;
            int toleranceSumSecondary = 0;
            foreach (MarkerInImage primaryMarker in this.markerSamplesFor3DMarkerCalibration.Keys)
            {
                colorRSumPrimary += primaryMarker.Color.R * primaryMarker.SearchShadeTolerance;
                colorGSumPrimary += primaryMarker.Color.G * primaryMarker.SearchShadeTolerance;
                colorBSumPrimary += primaryMarker.Color.B * primaryMarker.SearchShadeTolerance;
                toleranceSumPrimary += primaryMarker.SearchShadeTolerance;

                MarkerInImage secondaryMarker = markerSamplesFor3DMarkerCalibration[primaryMarker];
                colorRSumSecondary += secondaryMarker.Color.R * secondaryMarker.SearchShadeTolerance;
                colorGSumSecondary += secondaryMarker.Color.G * secondaryMarker.SearchShadeTolerance;
                colorBSumSecondary += secondaryMarker.Color.B * secondaryMarker.SearchShadeTolerance;
                toleranceSumSecondary += secondaryMarker.SearchShadeTolerance;
            }

            byte averageSearchTolerancePrimary = (byte)(toleranceSumPrimary / this.markerSamplesFor3DMarkerCalibration.Count);
            byte averageExtendTolerancePrimary = (byte)(averageSearchTolerancePrimary * 5 / 4);
            Color averageColorPrimary = Color.FromArgb(colorRSumPrimary / toleranceSumPrimary, colorGSumPrimary / toleranceSumPrimary, colorBSumPrimary / toleranceSumPrimary);
            MarkerInImage averageMarkerPrimary = new MarkerInImage(
                "Marker2D-" + averageColorPrimary.R + "-" + averageColorPrimary.G + "-" + averageColorPrimary.B + "-" + GenerateId(),
                "Marker2D-" + averageColorPrimary.R + "-" + averageColorPrimary.G + "-" + averageColorPrimary.B,
                averageColorPrimary,
                averageSearchTolerancePrimary,
                averageExtendTolerancePrimary,
                averageColorPrimary);

            byte averageSearchToleranceSecondary = (byte)(toleranceSumSecondary / this.markerSamplesFor3DMarkerCalibration.Count);
            byte averageExtendToleranceSecondary = (byte)(averageSearchTolerancePrimary * 5 / 4);
            Color averageColorSecondary = Color.FromArgb(colorRSumSecondary / toleranceSumSecondary, colorGSumSecondary / toleranceSumSecondary, colorBSumSecondary / toleranceSumSecondary);
            MarkerInImage averageMarkerSecondary = new MarkerInImage(
                "Marker2D-" + averageColorSecondary.R + "-" + averageColorSecondary.G + "-" + averageColorSecondary.B + "-" + GenerateId(),
                "Marker2D-" + averageColorSecondary.R + "-" + averageColorSecondary.G + "-" + averageColorSecondary.B,
                averageColorSecondary,
                averageSearchToleranceSecondary,
                averageExtendToleranceSecondary,
                averageColorSecondary);

            return (new KeyValuePair<MarkerInImage, MarkerInImage>(averageMarkerPrimary, averageMarkerSecondary));
        }

        public static string GenerateId()
        {
            string alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            string id = "";
            for (int i = 0; i < 8; i++)
            {
                string lucky = alphabet.Substring(RANDOM.Next(alphabet.Length), 1);
                id += lucky;
            }

            return id;
        }

        
        
        public void AddMarkerToMarkers3D(KeyValuePair<MarkerInImage, MarkerInImage> markerPair)
        {
            this.markersFor3D.Add(markerPair.Key, markerPair.Value);
            this.markersListChangedCall();
        }

        public void MarkersList_AddChangedHandler(MarkerListChangedHandler handler)
        {
            this.markersListChangedCall += handler;
        }
        public delegate void MarkerListChangedHandler();
        private MarkerListChangedHandler markersListChangedCall;



        /* Getters/setters: */

        public FilterInfoCollection AvailableVideoDeviceList
        {
            get { return availableVideoDeviceList; }
        }

        public Dictionary<string, MultiColorImageProcessor> ImageProcessors
        {
            get { return imageProcessors; }            
        }

        public Processor3D ThreeDProcessor
        {
            get { return this._3DProcessor; }
        }

        public Dictionary<string, VideoSource> VideoSources
        {
            get { return this.videoSources; }
        }

        public int VideoDeviceSelection
        {
            get { return videoDeviceSelection; }
            set { videoDeviceSelection = value; }
        }

        public MultiColorImageProcessor SourceProcessor1For3DSelection
        {
            get { return sourceProcessor1For3DSelection; }
            set { sourceProcessor1For3DSelection = value; }
        }

        public MultiColorImageProcessor SourceProcessor2For3DSelection
        {
            get { return sourceProcessor2For3DSelection; }
            set { sourceProcessor2For3DSelection = value; }
        }

        public Vector3 DXCameraPosition
        {
            get
            {
                this.UpdateDXCameraPosition();
                return dXCameraPosition;
            }   
        }
        
        public Vector3 DXCameraDirection
        {
            get { return dXCameraDirection; }
        }         

        public void RotateDXCamera(float byY, float byX)
        {
            if (this.mouseLookAndMovementEnabled)
            {
                Vector3 direction = this.dXCameraDirection;
                Vector3 normal = new Vector3(dXCameraDirection.Z, 0, -dXCameraDirection.X);
                Microsoft.DirectX.Matrix rotationMatrix = Microsoft.DirectX.Matrix.RotationY(byY);
                rotationMatrix.Multiply(Matrix.RotationAxis(normal, byX));
                direction.TransformCoordinate(rotationMatrix);
                this.dXCameraDirection = direction;
                this.UpdateDXCameraVelocity();
            }
        }

        public bool DXCameraIsMovingForward
        {
            get { return dXCameraIsMovingForward; }
            set
            {
                if (this.mouseLookAndMovementEnabled)
                {
                    dXCameraIsMovingForward = value;
                    this.UpdateDXCameraVelocity();
                }
            }
        }

        public bool DXCameraIsMovingBackward
        {
            get { return dXCameraIsMovingBackward; }
            set
            {
                if (this.mouseLookAndMovementEnabled)
                {
                    dXCameraIsMovingBackward = value;
                    this.UpdateDXCameraVelocity();
                }
            }
        }

        public bool DXCameraIsStrafingRight
        {
            get { return dXCameraIsStrafingRight; }
            set
            {
                if (this.mouseLookAndMovementEnabled)
                {
                    dXCameraIsStrafingRight = value;
                    this.UpdateDXCameraVelocity();
                }
            }
        }

        public bool DXCameraIsStrafingLeft
        {
            get { return dXCameraIsStrafingLeft; }
            set
            {
                if (this.mouseLookAndMovementEnabled)
                {
                    dXCameraIsStrafingLeft = value;
                    this.UpdateDXCameraVelocity();
                }
            }
        }

        public bool MouseLookAndMovementEnabled
        {
            get { return mouseLookAndMovementEnabled; }
            set
            {
                mouseLookAndMovementEnabled = value;
                MouseInterceptor.TrackMouseMovement = value;
            }
        }

        /* Marker Calibration */
        public System.Drawing.Color CalibratingMarkerPrimaryColor
        {
            get { return calibratingMarkerPrimaryColor; }
            set { calibratingMarkerPrimaryColor = value; }
        }
        public System.Drawing.Color CalibratingMarkerSecondaryColor
        {
            get { return calibratingMarkerSecondaryColor; }
            set { calibratingMarkerSecondaryColor = value; }
        }

        public PointInt2D CalibratingMarkerPrimaryPosition
        {
            get { return calibratingMarkerPrimaryPosition; }
            set { calibratingMarkerPrimaryPosition = value; }
        }
        public PointInt2D CalibratingMarkerSecondaryPosition
        {
            get { return calibratingMarkerSecondaryPosition; }
            set { calibratingMarkerSecondaryPosition = value; }
        }

        public float SampleBadnessPrimary
        {
            get { return sampleBadnessPrimary; }
            set { sampleBadnessPrimary = value; }
        }
        public float SampleBadnessSecondary
        {
            get { return sampleBadnessSecondary; }
            set { sampleBadnessSecondary = value; }
        }

        public bool CalibratingMarkerPrimarySelected
        {
            get { return calibratingMarkerPrimarySelected; }
            set { calibratingMarkerPrimarySelected = value; }
        }
        public bool CalibratingMarkerSecondarySelected
        {
            get { return calibratingMarkerSecondarySelected; }
            set { calibratingMarkerSecondarySelected = value; }
        }

        public byte TolerancePrimaryForMarkerCalibration
        {
            get { return this.tolerancePrimaryForMarkerCalibration; }
            set { this.tolerancePrimaryForMarkerCalibration = value; }
        }
        public byte ToleranceSecondaryForMarkerCalibration
        {
            get { return this.toleranceSecondaryForMarkerCalibration; }
            set { this.toleranceSecondaryForMarkerCalibration = value; }
        }

        public Dictionary<MarkerInImage, MarkerInImage> MarkersFor3D
        {
            get { return markersFor3D; }
        }

        public FilterInfo GetVideoSourceSelectionPrimaryForMarkerCalibration()
        {
            return videoSourceSelectionPrimaryForMarkerCalibration;
        }
        public void SetVideoSourceSelectionPrimaryForMarkerCalibration(FilterInfo fi)
        {
            videoSourceSelectionPrimaryForMarkerCalibration = fi;
        }
        public FilterInfo GetVideoSourceSelectionSecondaryForMarkerCalibration()
        {
            return videoSourceSelectionSecondaryForMarkerCalibration;
        }
        public void SetVideoSourceSelectionSecondaryForMarkerCalibration(FilterInfo fi)
        {
            videoSourceSelectionSecondaryForMarkerCalibration = fi;
        }
  
        public void SetBitmapInPrimaryVideoPictureboxForMarkerCalibration(Bitmap bitmap)
        {
            this.bitmapOnPrimaryVideoPictureboxForMarkerCalibration = bitmap;
        }
        public Bitmap GetBitmapInPrimaryVideoPictureboxForMarkerCalibration()
        {
            return bitmapOnPrimaryVideoPictureboxForMarkerCalibration;
        }

        public void SetBitmapInSecondaryVideoPictureboxForMarkerCalibration(Bitmap bitmap)
        {
            this.bitmapOnSecondaryVideoPictureboxForMarkerCalibration = bitmap;
        }
        public Bitmap GetBitmapInSecondaryVideoPictureboxForMarkerCalibration()
        {
            return bitmapOnSecondaryVideoPictureboxForMarkerCalibration;
        }

        public void SetByteImageInPrimaryVideoPictureboxForMarkerCalibration(byte[] byteImage)
        {
            byteImageOnPrimaryVideoPictureboxForMarkerCalibration = byteImage;
        }
        public byte[] GetByteImageInPrimaryVideoPictureboxForMarkerCalibration()
        {
            return byteImageOnPrimaryVideoPictureboxForMarkerCalibration;
        }
        public void SetByteImageInSecondaryVideoPictureboxForMarkerCalibration(byte[] byteImage)
        {
            byteImageOnSecondaryVideoPictureboxForMarkerCalibration = byteImage;
        }
        public byte[] GetByteImageInSecondaryVideoPictureboxForMarkerCalibration()
        {
            return byteImageOnSecondaryVideoPictureboxForMarkerCalibration;
        }

        /* for StereoProcessing*/

        public FilterInfo GetVideoSourceSelectionPrimaryFor3DProcessing()
        {
            return videoSourceSelectionPrimaryFor3DProcessing;            
        }
        public void SetVideoSourceSelectionPrimaryFor3DProcessing(FilterInfo fi)
        {
            videoSourceSelectionPrimaryFor3DProcessing = fi;
        }
        public FilterInfo GetVideoSourceSelectionSecondaryFor3DProcessing()
        {
            return videoSourceSelectionSecondaryFor3DProcessing;            
        }
        public void SetVideoSourceSelectionSecondaryFor3DProcessing(FilterInfo fi)
        {
            videoSourceSelectionSecondaryFor3DProcessing = fi;
        }
        

        

        /* Attributes: */

        /**
         * A list of video input devices such as cameras, webcams and virtual video devices attached to your computer.
         * Some aspects are stored for every device such as available capturing resolutions, fps and ids.
         */
        private FilterInfoCollection availableVideoDeviceList;

        /**
         * In other words the cameras (webcams or other video input devices) that are capturing or are
         * prepared to be capturing.
         * We need to use Dictionary in order to be able to manage the VideoSources by their names (ids).
         */
        private Dictionary<string, VideoSource> videoSources;

        /**
         * Units that will analyze the result of the capturing of each cameras. These are the objects that
         * are searching for colored markers on images and give results as coordinates.
         * We need to use Dictionary to be able to manage the ImageProcessors by their connected VideoSources' ids (names).
         */
        private Dictionary<string, MultiColorImageProcessor> imageProcessors;

        /**
         * Thread corresponding to the ImageProcessor corresponding to the VideoSource.
         * We need to use Dictionary to be able to manage the threads by their connected (ImageProcessors and) VideoSources' ids (names).
         */
        private Dictionary<string, Thread> imageProcessorThreads;

        private Processor3D _3DProcessor;

        private Thread _3DProcessorThread;

        /**
         * The index of the selected video device from the availableVideoDeviceList.
         */
        private int videoDeviceSelection = 1;

        /**
         * The identification string of the device that is selected for 3D position processing as first/second source device.
         */
        private MultiColorImageProcessor sourceProcessor1For3DSelection;
        private MultiColorImageProcessor sourceProcessor2For3DSelection;

        /**
         * Camera controls in DirecX environment.
         */
        private Vector3 dXCameraPosition;
        private Vector3 dXCameraDirection;
        private Vector3 dXCameraVelocity;

        private bool dXCameraIsMovingForward;
        private bool dXCameraIsMovingBackward;
        private bool dXCameraIsStrafingRight;
        private bool dXCameraIsStrafingLeft;

        private System.Diagnostics.Stopwatch dxCameraStopwatch = new System.Diagnostics.Stopwatch();

        private bool mouseLookAndMovementEnabled;

        /* Marker calibration */
        private FilterInfo videoSourceSelectionPrimaryForMarkerCalibration;
        private FilterInfo videoSourceSelectionSecondaryForMarkerCalibration;
        private System.Drawing.Bitmap bitmapOnPrimaryVideoPictureboxForMarkerCalibration;
        private System.Drawing.Bitmap bitmapOnSecondaryVideoPictureboxForMarkerCalibration;
        private byte[] byteImageOnPrimaryVideoPictureboxForMarkerCalibration;
        private byte[] byteImageOnSecondaryVideoPictureboxForMarkerCalibration;
        private System.Drawing.Color calibratingMarkerPrimaryColor;
        private System.Drawing.Color calibratingMarkerSecondaryColor;
        private PointInt2D calibratingMarkerPrimaryPosition;
        private PointInt2D calibratingMarkerSecondaryPosition;
        private float sampleBadnessPrimary;
        private float sampleBadnessSecondary;
        private bool calibratingMarkerPrimarySelected;
        private bool calibratingMarkerSecondarySelected;
        private byte tolerancePrimaryForMarkerCalibration;
        private byte toleranceSecondaryForMarkerCalibration;
        private Dictionary<MarkerInImage, MarkerInImage> markerSamplesFor3DMarkerCalibration;
        private Dictionary<MarkerInImage, MarkerInImage> markersFor3D;

        /* 3D processing */
        private FilterInfo videoSourceSelectionPrimaryFor3DProcessing;
        private FilterInfo videoSourceSelectionSecondaryFor3DProcessing;
    }
}
